package protocol

import (
	"encoding/hex"
	"fmt"
	"gitee.com/crack007/goose"
	"gitee.com/crack007/goose/core/server"
	"gitee.com/crack007/udp-penetrate/src/api/config"
	"gitee.com/crack007/udp-penetrate/src/common/constant"
	"net"
)

// server通知目标客户端发送一个握手消息给发起端
type ClientHandshakeReqProtocol struct {
	TargetHost string // 连接目标的公网ip
	TargetPort uint16 // 连接目标的公网端口
	OriginHost string // 发起的客户端公网ip
	OriginPort uint16 // 发起的客户端公网端口
	OriginUid  uint32 // 发起的客户端uid
}

func NewClientHandshakeReqProtocol() *ClientHandshakeReqProtocol {
	return &ClientHandshakeReqProtocol{}
}

func (p *ClientHandshakeReqProtocol) Code() uint32 {
	return constant.CMD_CLIENTHANDSHAKE_REQ
}

// client端执行
func (p *ClientHandshakeReqProtocol) Process(session *server.Session, clientConn *net.UDPConn) IProtocol {
	// 往发起的客户端发消息
	udpConn, err := net.DialUDP("udp", &net.UDPAddr{Port: int(config.ArgConfig.LocalPort)}, &net.UDPAddr{IP: net.ParseIP(p.OriginHost), Port: int(p.OriginPort)})
	if err != nil {
		goose.GetLogger().Warn("DialUDP err=%s", err.Error())
		return nil
	}
	go func() {
		// 发送一个探测消息给发起的客户端，再通知server连接已准备好
		_, err := udpConn.Write([]byte("hello"))
		if err != nil {
			goose.GetLogger().Warn("udp探测失败 err=%s", err.Error())
			return
		}
		clientHandshakeRespProtocol := NewClientHandshakeRespProtocol()
		clientHandshakeRespProtocol.TargetPort = p.TargetPort
		clientHandshakeRespProtocol.TargetHost = p.TargetHost
		clientHandshakeRespProtocol.OriginHost = p.OriginHost
		clientHandshakeRespProtocol.OriginPort = p.OriginPort
		clientHandshakeRespProtocol.Uid = p.OriginUid
		// server已经关闭，需要重新建立连接
		serverConn, err := net.Dial("udp", fmt.Sprintf("%s:%d", config.AppConfig.ServerHost, config.AppConfig.ServerPort))
		if err != nil {
			goose.GetLogger().Warn("Connect server error-> %s", err.Error())
			return
		}
		data, _ := PackData(clientHandshakeRespProtocol)
		_, err = serverConn.Write(data)
		if err != nil {
			goose.GetLogger().Warn("Send server error-> %s", err.Error())
			return
		}
	}()
	for {
		var buf = make([]byte, 0xffff)
		n, err := udpConn.Read(buf)
		if err != nil {
			goose.GetLogger().Warn("ReadFromUDP err=%s", err.Error())
			return nil
		}
		goose.GetLogger().Debug("receive data \r\n%s", hex.Dump(buf[:n]))
		iProtocol, err := UnpackData(buf[:n])
		if err != nil {
			return nil
		}
		resp := iProtocol.Process(nil, udpConn)
		if resp != nil {
			packData, err := PackData(resp)
			if err != nil {
				return nil
			}
			_, err = udpConn.Write(packData)
			if err != nil {
				goose.GetLogger().Warn("WriteTo err=%s", err.Error())
				return nil
			}
		}
	}
}

func (p *ClientHandshakeReqProtocol) Marshall() []byte {
	return marshalHostMeta(&hostMeta{
		TargetHost: p.TargetHost,
		TargetPort: p.TargetPort,
		OriginHost: p.OriginHost,
		OriginPort: p.OriginPort,
		Uid:        p.OriginUid,
	})
}

func (p *ClientHandshakeReqProtocol) UnMarshall(data []byte) {
	meta := unMarshalHostMeta(data)
	p.OriginUid = meta.Uid
	p.TargetPort = meta.TargetPort
	p.TargetHost = meta.TargetHost
	p.OriginHost = meta.OriginHost
	p.OriginPort = meta.OriginPort
}
